ios - 활성화할 - 애플뮤직 해지




새로운 Apple Music 앱과 동일한 동적 상태 표시 줄 (3)

99.99 %는 공개 API (쉽게)를 사용하여 수행 할 수 없다는 것을 확신합니다. 개인적으로 거의 모든 것을 시도했기 때문에 (개인적으로는 자신의 상태 표시 줄에 마법 같은 방법이라고 생각하지 않지만 대신에 응용 프로그램을 사용할 수 있습니다. 상태 표시 줄보기를 검색 한 다음 마스크 만 적용).

내가 확신 할 수있는 것은 자신의 StatusBar를 할 수 있으며, 불행히도 아주 오래된 하나의 MTStatusBarOverlay 라이브러리가 있기 때문에 실제로 작동하는지는 알 수 없지만 여전히 사용하는 사람들이있는 것처럼 보입니다.

그러나 라이브러리가 그것을하는 방법을 사용하여, 나는 확실한 해결책이있을 것이라고 생각합니다. 많은 일을 필요로하지만 "살 수는 없지만"가능합니다. 요약하면 다음과 같습니다.

  • 상위 20 픽셀 (상태 표시 줄)의 스크린 샷 가져 오기
  • 그 스크린 샷에서 검은 색이 아닌 모든 것을 제거하십시오 (검정색 모서리를 검색하여 녹색 배터리와 투명도를 유지할 수 있도록 개선 할 수 있습니다). 이렇게하면 오버레이 마스크와 가짜 상태 막대가 만들어집니다
  • 오버레이 상태 표시 줄 : 배경 뷰 마스킹 실제 상태 표시 줄 및 방금 생성 한 알파 이미지
  • 해당 이미지에 마스크를 적용하면 마스크 된 모든 것이 색상이 흰색 음영으로 바뀝니다.
  • 사용자 스크롤에 따라 마스크 높이 변경

이제 제대로 스크롤하고 색상을 바꿀 수 있어야합니다. 유일한 문제는 상태 표시 줄이 살아 있지 않다는 것입니다. 스크롤 아웃하면 즉시 오버레이를 제거하여 새로 고칠 수 있습니다. 맨 위로 스크롤 할 때도 똑같은 작업을 수행하지만 상태 표시 줄의 색상을 흰색 (애니메이션 없음)으로 변경하여 상태에 맞 춥니 다. 짧은 기간 동안 만 라이브가되지 않습니다.

희망이 도움이됩니다!

새로운 Apple Music 앱 에있는 statusBar 를 동적으로 채색하는 것이 가능합니까?

편집하다:

iOS 8.4의 새로운 Apple 뮤직 앱에는이 기능이 있습니다.

  • 앱을 엽니 다.
  • 노래 선택 및 재생 (상태 표시 줄은 흰색 임)
  • 플레이어 컨트롤러를 아래로 스 와이프하여 "내 음악"컨트롤러를 봅니다 (검은 색 상태 표시 줄이 있습니다. 아마도 탐색 계층으로 돌아 가야 할 것입니다).
  • 이제 위 / 아래로 스 와이프하면 동적 상태 표시 줄이 변경됩니다.

편집 2 :

애플의 문서는 우리가 지금 그것을 사용하는 것을 보이지 않는다 ( iOS 8.4 ). iOS 9 에서 향후 제공 될 예정입니다.

편집 3 : 아직 iOS 9 에서 사용할 수없는 것 같습니다.


Jiri의 대답을 반복하면, 이것은 당신을 아주 가깝게 할 것입니다. MTStatusBarOverlayMTStatusBarOverlayCWStatusBarNotification . 뷰 컨트롤러 간의 모달 전환을 처리하기 위해 MusicPlayerTransition 사용하고 있습니다. 우리는 frame : CGRect (0, 0, self.view.bounds.size.width, self.view.bounds.size.width)가있는 self.view에서 imageView : "art"를 가정합니다. 약간의 마사지가 필요하지만, 당신은 요지를 얻습니다. 참고 : 우리는 "생방송"이 아니지만 대부분은 1 초이며 배터리 색상은 보존되지 않습니다. 또한 CWStatusBarNotification.m의 애니메이션 시간을 0으로 설정해야합니다. (notificationAnimationDuration 속성).

#import "CWStatusBarNotification.h"

#define kStatusTextOffset       5.4  // (rough guess of) space between window's origin.y and status bar label's origin.y

@interface M_Player () <UIGestureRecognizerDelegate> 

@property (retain) UIView *fakeStatusBarView;
@property (retain) CWStatusBarNotification *fakeStatusBar;
@property (retain) UIImageView *statusImgView;
@property (retain) UIImageView *statusImgViewCopy;
@property (retain) UIWindow *window;
@property (strong, nonatomic) NSTimer *statusTimer;

@end

@implementation M_Player
@synthesisze fakeStatusBarView, fakeStatusBar, statusImgView, statusImgViewCopy, window, statusTimer;

-(void)viewDidLoad{

    self.window = [[UIApplication sharedApplication] delegate].window;

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleStatusBarDrag:)];
    pan.delegate = self;
    [self.view addGestureRecognizer:pan];
}

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    if (!fakeStatusBar){
        [self buildFakeStatusBar];
    }

    if (!statusTimer) {
        [self setupStatusBarImageUpdateTimer];
    }

     // optional
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
    [self setNeedsStatusBarAppearanceUpdate]; 


-(void)viewDidDisappear:(BOOL)animated{
   [super viewDidDisappear:animated];

   [self destroyStatusBarImageUpdateTimer];

}

-(void)destroyFakeStatusBar{
    [statusImgView removeFromSuperview];
    statusImgView = nil;
    [fakeStatusBarView removeFromSuperview];
    fakeStatusBarView = nil;
    fakeStatusBar = nil;
}

-(void)buildFakeStatusBar{
    UIWindow *statusBarWindow =  [[UIApplication sharedApplication] valueForKey:@"_statusBarWindow"];  // This window is actually still fullscreen. So we need to capture just the top 20 points.

    UIGraphicsBeginImageContext(self.view.bounds.size);
    [statusBarWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, 20);
    CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
    UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    statusImg = [statusImg imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];  // This allows us to set the status bar content's color via the imageView's .tintColor property

    statusImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    statusImgView.image = statusImg;
    statusImgView.tintColor = [UIColor colorWithWhite:0.859 alpha:1.000];  // any color you want

    statusImgViewCopy = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    statusImgViewCopy.image = statusImg;
    statusImgViewCopy.tintColor = statusImgView.tintColor;


    fakeStatusBarView = nil;
    fakeStatusBar = nil;
    fakeStatusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
    [fakeStatusBarView addSubview:statusImgView];

    fakeStatusBar = [CWStatusBarNotification new];
    fakeStatusBar.notificationStyle = CWNotificationStyleStatusBarNotification;
    [fakeStatusBar displayNotificationWithView:fakeStatusBarView forDuration:CGFLOAT_MAX];
}

-(void)handleStatusBarDrag:(UIPanGestureRecognizer*)gestureRecognizer{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

    }

    if (gestureRecognizer.state == UIGestureRecognizerStateChanged){
        CGPoint convertedPoint = [self.window convertPoint:art.frame.origin fromView:self.view];
        CGFloat originY = convertedPoint.y - kStatusTextOffset;

        if (originY > 0 && originY <= 10) {  // the range of change we're interested in
            //NSLog(@"originY:%f statusImgView.frame:%@", originY, NSStringFromCGRect(statusImgView.frame));

            // render in context from new originY using our untouched copy as reference view
            UIGraphicsBeginImageContext(self.view.bounds.size);
            [statusImgViewCopy.layer renderInContext:UIGraphicsGetCurrentContext()];
            UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            CGRect rect = CGRectMake(0, kStatusTextOffset + originY, self.view.bounds.size.width, 20);
            CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
            UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
            CGImageRelease(imageRef);

            statusImgView.image = statusImg;
            statusImgView.transform = CGAffineTransformMakeTranslation(0, kStatusTextOffset + originY);

        }

       // destroy
        if (originY > 90) {
            [self destroyFakeStatusBar];
        }
    }

    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){

    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

실제 상태 표시 줄과 상태 표시 줄 스크린 샷을 동기화하려면 타이머를 설정하십시오. viewWillAppear에서 발사하고 viewDidDisappear에서 종료합니다.

-(void)setupStatusBarImageUpdateTimer{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^(){
            // main thread
            if (!statusTimer) {
                statusTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleStatusTimer:) userInfo:nil repeats:YES];
                [[NSRunLoop currentRunLoop] addTimer:statusTimer forMode:NSRunLoopCommonModes];
            }
        });
    });
}

-(void)destroyStatusBarImageUpdateTimer{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^(){
            // main thread
            [statusTimer invalidate];
            statusTimer = nil;
        });
    });
}

-(void)handleStatusTimer:(NSTimer*)timer{
    UIWindow *statusBarWindow =  [[UIApplication sharedApplication] valueForKey:@"_statusBarWindow"];

    UIGraphicsBeginImageContext(CGSizeMake(self.view.bounds.size.width, 20));
    [statusBarWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGRect rect = CGRectMake(0, 0, self.view.bounds.size.width, 20);
    CGImageRef imageRef = CGImageCreateWithImageInRect([viewImage CGImage], rect);
    UIImage *statusImg = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    statusImg = [statusImg imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
    statusImgViewCopy.image = statusImg;
}

타이머에 대한 강력한 참조가 있고 동일한 스레드에서 설정 및 무효화가 발생하기 때문에 타이머가 무효화되지 않아도 걱정할 필요가 없습니다. 최종 결과는 다음과 같아야합니다.


언뜻보기에는 상태 표시 줄에서 스냅 샷을 조작 한 것처럼 보이지만 상태 표시 줄은 양 끝 부분에 있습니다. 따라서 그렇지 않습니다.

얼핏보기에 iOS 8.4에 도입 된 새로운 API처럼 보였지만 API를 검토 한 후에는 관련 항목을 찾을 수 없었습니다.

사과가 자신의 앱에서 개인 apis를 사용한다는 것은 매우 이상한 것 같습니다. 이것은 개발자를위한 나쁜 예가 될 것입니다. 그러나 다시 말하지만 라이브 상태 표시 줄에 두 가지 스타일을 사용할 수있는 공개 된 것은 없습니다.

이로 인해 우리는 개인적으로 api 나 black magic을 남겨 둡니다.





statusbar